using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.IO;
using GHIElectronics.NETMF.IO;

namespace Soigeneris
{
    /// <summary>
    /// Reads a Test Configuration in from an XML file
    /// </summary>
    class ReadXML
    {
        static TestConfig config; // Container for the Test Configuration

        /// <summary>
        /// Entry point for reading in XML file.
        /// TODO: Need to have some way of passing in file name.
        /// </summary>
        /// <returns>Test Configuration read from XML</returns>
        public static TestConfig Read(String file)
        {
            // Check to see if SD Card is inserted
            while (!PersistentStorage.DetectSDCard())
            {
                //TODO: Print this to LCD or move this function to Program
                Debug.Print("Please Insert SD card!");
                Thread.Sleep(1000);
            }

            // Create a new storage device, and mount file system
            PersistentStorage sdPS = new PersistentStorage("SD");
            sdPS.MountFileSystem();
            Thread.Sleep(3000); // some cards will cause a crash without a delay
            string rootDirectory = VolumeInfo.GetVolumes()[0].RootDirectory;
            FileStream FileHandle = new FileStream(rootDirectory + file,
                                            FileMode.Open, FileAccess.Read);

            // configure XML reader
            XmlReaderSettings ss = new XmlReaderSettings();
            ss.IgnoreComments = true;
            ss.IgnoreWhitespace = true;

            // Grab our instance of the Test configuration class
            // if we want to load a new test then this instance will 
            // need to be released.
            config = TestConfig.Instance;

            // Create an XML reader for this file.
            using (XmlReader reader = XmlReader.Create(FileHandle, ss))
            {
                if (reader.ReadToFollowing("TestConfig")) { Debug.Print("Test Name"); }
                if (reader.ReadToFollowing("PinMap")) { Debug.Print("PinMap"); }
                readPinMappings(reader);
                conListPrint();
                if (reader.ReadToFollowing("TestCase"))
                {
                    readTest(reader);
                    testListPrint();
                }
            }

            // unmount file system
            sdPS.UnmountFileSystem();
            return config;
        }

        // need to error out if the format of the XML is not correct
        // also we are assuming that the connectors will be in numerical order

        /// <summary>
        /// Reads in the inforamtion about the connectors and their pin mappings
        /// </summary>
        /// <param name="reader">The XML reader to use</param>
        private static void readPinMappings(XmlReader reader)
        {
            int pin;
            TestPoint testPoint;
            String conName;

            if (reader.ReadToFollowing("Connector"))
            {
                do
                {
                    conName = reader.GetAttribute("Name"); // i.e. 'C1'
                    pin = Convert.ToInt16(reader.GetAttribute("Pins"));
                    TestConfig.Connector con = new TestConfig.Connector(conName, pin);
                    if (reader.ReadToFollowing("Pin"))
                    {
                        do
                        {
                            reader.ReadToDescendant("Num");
                            pin = Convert.ToInt16(reader.ReadElementString());
                            testPoint = (TestPoint)Convert.ToInt16(reader.ReadElementString("TestPoint"));
                            con.Add(pin, testPoint);
                        } while (reader.ReadToNextSibling("Pin"));
                    }
                    config.AddConnector(con);// add Connector to list
                } while (reader.ReadToNextSibling("Connector"));
            }
        }

        /// <summary>
        /// Reads in a list of tests to run.
        /// </summary>
        /// <param name="reader">XML Reader</param>
        private static void readTest(XmlReader reader)
        {
            if (reader.ReadToFollowing("Test"))
            {
                do{
                    TestConfig.Test test = new TestConfig.Test(reader.GetAttribute("Name"));
                    if (reader.ReadToDescendant("Drive")
                        & reader.ReadToFollowing("Connector"))
                    {
                        ArrayList pinList = readConnector(reader);
                        for (int i = 0; i < pinList.Count; i++)
                        {
                            test.DrivePin =(TestConfig.ConnectorPinMap)pinList[i];
                        }
                    }

                    if (reader.ReadToFollowing("Driven")
                        & reader.ReadToFollowing("Connector"))
                    {
                        do
                        {   // add a new connector to list
                            ArrayList pinList = readConnector(reader);
                            for (int i = 0; i < pinList.Count; i++)
                            {
                                if (!test.AddDrivenPin((TestConfig.ConnectorPinMap)pinList[i]))
                                {
                                    Debug.Print("This pin combination does not exist!");
                                }

                            }
                        } while (reader.ReadToNextSibling("Connector"));
                    }
                    config.AddTest(test);  // this is the test
                    reader.Read();
                } while (reader.ReadToNextSibling("Test"));
            }
        }

        /// <summary>
        /// Read in a connector name and list of its pins that should be connected.
        /// </summary>
        /// <param name="reader">XML Reader</param>
        /// <returns>ArrayList of PinMaps</returns>
        private static ArrayList readConnector(XmlReader reader)
        {
            int pin;
            ArrayList list = new ArrayList();
            String name = reader.GetAttribute("Name");
            TestConfig.Connector con = config.GetConnector(name);

            if (reader.ReadToFollowing("Pin"))
            {
                do
                {
                    pin = (int)Convert.ToInt16(reader.ReadString());
                    list.Add(new TestConfig.ConnectorPinMap(con.Name, pin));
                } while (reader.ReadToNextSibling("Pin"));
            }

            return list;
        }

        /// <summary>
        ///  Diagnostic helper to print ConnetorList to console
        /// </summary>
        private static void conListPrint()
        {
            int conTot = config.ConnectorCount;
            Debug.Print("# Con: " + conTot.ToString());
            for (int i = 0; i < conTot; i++)
            {
                // grab the connector name/number here
                TestConfig.Connector con = config.GetConnector(i);
                for (int j = 1; j < con.PinCount +1; j++)
                {
                    Debug.Print("Connector " + con.Name + " Pin: " + (j + 1).ToString()
                        + " TestPoint: " + con.Pin2Point(j));
                }
            }
        }

        /// <summary>
        /// Helper to print TestList to console
        /// </summary>
        private static void testListPrint()
        {
            int testTot = config.TestCount;
            Debug.Print(" ");
            Debug.Print("# Test: " + testTot.ToString());
            for (int i = 0; i < testTot; i++)
            {
                TestConfig.Test test = config.GetTest(i);
                Debug.Print(" ");
                Debug.Print("Test name - " + test.Name);
                Debug.Print("Driver -  Con: " + test.DrivePin.ConnectorName
                   + " Pin: " + (test.DrivePin.Pin).ToString());
                Debug.Print(" # Driven Pins: " + test.DrivenPinCount);

                for (int j = 0; j < test.DrivenPinCount; j++)
                {
                    TestConfig.ConnectorPinMap map = config.Point2PinMap(test.GetTestPoint(j));
                    Debug.Print("  Connector " + map.ConnectorName.ToString()
                        + " Pin " + (map.Pin).ToString());
                }
            }
        }

    }
}
